use std::io::{self,};

macro_rules! write_number {
    ($method:ident, $integer:ty, $new_expr:expr) => {
        #[inline]
        fn $method<W>(&mut self, writer: &mut W, value: $integer) -> io::Result<()> 
        where 
            W: ?Sized + io::Write 
        {
            let mut buffer = $new_expr; 
            let s = buffer.format(value);
            writer.write_all(s.as_bytes())
        }
    }
}

macro_rules! write_char_function {
    ($method:ident, $char:literal) => {
        #[inline]
        fn $method<W>(&mut self, writer: &mut W) -> io::Result<()> 
        where 
            W: ?Sized + io::Write 
        {
            writer.write_all($char)
        }
    };
}

pub trait Formatter {


    #[inline]
    fn write_null<W>(&mut self, writer: &mut W) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    {
        writer.write_all(b"null")
    }

    #[inline]
    fn write_bool<W>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
    where 
        W: ?Sized + io::Write
    {
        writer.write_all(if value { &[b'1'] } else { &[b'0'] })
    }

    write_number!(write_i8,     i8,  itoa::Buffer::new());
    write_number!(write_i16,    i16, itoa::Buffer::new());
    write_number!(write_i32,    i32, itoa::Buffer::new());
    write_number!(write_i64,    i64, itoa::Buffer::new());
    write_number!(write_i128,   i128,itoa::Buffer::new());

    write_number!(write_u8,     u8,  itoa::Buffer::new());
    write_number!(write_u16,    u16, itoa::Buffer::new());
    write_number!(write_u32,    u32, itoa::Buffer::new());
    write_number!(write_u64,    u64, itoa::Buffer::new());
    write_number!(write_u128,   u128,itoa::Buffer::new());

    write_number!(write_f32,    f32, ryu::Buffer::new());
    write_number!(write_f64,    f64, ryu::Buffer::new());



    #[inline]
    fn write_number_str<W>(&mut self, writer: &mut W, value: &str) -> io::Result<()>
    where
        W: ?Sized + io::Write
    {
        writer.write_all(value.as_bytes())
    }

    #[inline]
    fn begin_string<W>(&mut self, _writer: &mut W) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    {
        Ok(())
    }
    #[inline]
    fn end_string<W>(&mut self, _writer: &mut W) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    { 
        Ok(())
    }
    // does't need any escaping char 
    #[inline]
    fn write_string_fragment<W>(&mut self, writer: &mut W, fragment: &str) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    {
        writer.write_all(fragment.as_bytes())
    }


    // ***** Array -related write ********

    write_char_function!(begin_array,   b"[");
    write_char_function!(end_array,     b"]");

    #[inline]
    fn begin_array_value<W>(&mut self, writer: &mut W, first: bool) -> io::Result<()>
    where
        W: ?Sized + io::Write
    {
        if first {
            Ok(())
        } else {
            writer.write_all(b",")
        }
    }
    #[inline]
    fn end_array_value<W>(&mut self, _writer: &mut W) -> io::Result<()>
    where
        W: ?Sized + io::Write
    {
        Ok(())
    }
    #[inline] 
    fn write_byte<W>(&mut self, writer: &mut W, byte: u8) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    {
        writer.write_all(&byte.to_le_bytes())
    }
    #[inline]
    fn write_byte_array<W>(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    {
        /*
        self.begin_array(writer)?;
        let mut first = true;
        for byte in value {
            self.begin_array_value(writer, first)?;
            self.write_u8(writer, *byte)?;
            self.end_array_value(writer)?;
            first = false;
        }
        self.end_array(writer)
        */
        for byte in value {
            self.write_byte(writer, *byte)?;
        }
        Ok(())
    }


    // ***** Object-related write ********

    write_char_function!(begin_object,   b"{");
    write_char_function!(end_object,     b"}");

    #[inline]
    fn begin_object_key<W>(&mut self, writer: &mut W, first: bool) -> io::Result<()> 
    where 
        W: ?Sized + io::Write 
    {
        if first {
            Ok(())
        } else {
            writer.write_all(b",")
        }
    }
    #[inline]
    fn end_object_key<W>(&mut self, _writer: &mut W) -> io::Result<()> 
    where 
        W: ?Sized + io::Write 
    {
        Ok(())
    }
    
    write_char_function!(begin_object_value,   b"=");

    #[inline]
    fn end_object_value<W>(&mut self, _writer: &mut W) -> io::Result<()> 
    where 
        W: ?Sized + io::Write
    {
        Ok(())
    }




    #[inline]
    fn write_char_escape<W>(&mut self, writer: &mut W, char_escape: CharEscape) -> io::Result<()> 
    where 
        W: ?Sized + io::Write 
    {
        use self::CharEscape::*;
        let s = match char_escape {
            ReverseSolidus => b"\\\\",
            Comma => b"\\,",
            Equals => b"\\=",
            OpeningBrackets => b"\\[",
            ClosingBrackets => b"\\]",
            OpeningCurlyBrackets => b"\\{",
            ClosingCurlyBrackets => b"\\}",
        };
        writer.write_all(s)
    }

    fn write_escaped_str<W>(&mut self, writer: &mut W, v: &str) -> io::Result<()>
    where 
        W: ?Sized + io::Write
    {
        let bytes = v.as_bytes();

        let mut start = 0;
        for( i, &byte) in bytes.iter().enumerate() {
            let escape = ESCAPE[byte as usize];
            if escape == 0  {
                continue ;
            }
            if i > start {
                self.write_string_fragment(writer, &v[start..i])?;
            }
            let char_escape = CharEscape::from_escape_table(escape);
            self.write_char_escape(writer, char_escape)?;


            start = i + 1;
        }
        if start != bytes.len() {
            self.write_string_fragment(writer, &v[start..])
        }else {
            Ok(())
        }
    }
}


pub enum CharEscape {
    // '\' 
    ReverseSolidus,
    // ','
    Comma,
    // '='
    Equals,
    // '['
    OpeningBrackets,
    // ']'
    ClosingBrackets,
    // '{'
    OpeningCurlyBrackets,
    // '}'
    ClosingCurlyBrackets,
}


const RS:  u8 = b'\\';  //0x5c
const CM:  u8 = b',';   //0x2c
const EQ:  u8 = b'=';   //0x3d
const OM:  u8 = b'[';   //0x5b  open middle br
const MM:  u8 = b']';   //0x5d  middle middle br
const OB:  u8 = b'{';   //0x7b  open big br
const CB:  u8 = b'}';   //0x7d  closing big br
const __:  u8 = 0;      //0x00

static ESCAPE: [u8; 256] = [
//  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //0
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //1
    __, __, __, __, __, __, __, __, __, __, __, __, CM, __, __, __, //2
    __, __, __, __, __, __, __, __, __, __, __, __, __, EQ, __, __, //3
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //4
    __, __, __, __, __, __, __, __, __, __, __, OM, RS, MM, __, __, //5
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //6
    __, __, __, __, __, __, __, __, __, __, __, OB, __, CB, __, __, //7
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //8
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //9
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //a
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //b
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //c
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //d
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //e
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, //f
];


impl CharEscape {
    #[inline]
    fn from_escape_table(escape: u8) -> CharEscape {
        match escape {
            RS  => CharEscape::ReverseSolidus,
            CM => CharEscape::Comma,
            EQ  => CharEscape::Equals,
            OM  => CharEscape::OpeningBrackets,
            MM  => CharEscape::ClosingBrackets,
            OB => CharEscape::OpeningCurlyBrackets,
            CB => CharEscape::ClosingCurlyBrackets,
            _   => unreachable!(),
        }
    }
}


//#[derive(Clone, Debug)]
pub struct CompactFormatter;
impl Formatter for CompactFormatter {}




#[cfg(test)]
mod test {
    use super::*;
    use crate::tracer::Tracer;

    struct MockFormatter {}
    impl Formatter for MockFormatter {
    }

    //TODO: use the macro to replace 
    #[test]
    fn write_null(){
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_null(&mut tracer);
        assert!(tracer.equal_to(b"null"));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_bool(&mut tracer, true);
        assert!(tracer.equal_to(b"1"));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_i64(&mut tracer, 64_i64);
        assert!(tracer.equal_to(b"64"));
            
        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_i32(&mut tracer, 32_i32);
        assert!(tracer.equal_to(b"32"));


        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_number_str(&mut tracer, "123456");
        assert!(tracer.equal_to(b"123456"));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_f32(&mut tracer, 3.222);
        assert!(tracer.equal_to(b"3.222"));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.begin_string(&mut tracer);
        assert!(tracer.equal_to(b""));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.end_string(&mut tracer);
        assert!(tracer.equal_to(b""));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_string_fragment(&mut tracer, "abcde");
        assert!(tracer.equal_to(b"abcde"));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.begin_array(&mut tracer);
        let _ = MockFormatter{}.begin_array_value(&mut tracer, true);
        let _ = MockFormatter{}.write_string_fragment(&mut tracer, "abcde");
        let _ = MockFormatter{}.end_array_value(&mut tracer);
        let _ = MockFormatter{}.begin_array_value(&mut tracer, false);
        let _ = MockFormatter{}.write_string_fragment(&mut tracer, "abcde");
        let _ = MockFormatter{}.end_array_value(&mut tracer);
        let _ = MockFormatter{}.end_array(&mut tracer);
        assert!(tracer.equal_to(b"[abcde,abcde]"));
        
        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.write_byte_array(&mut tracer, &[0x01,0x2,0x3,0x4,0x5]);
        assert!(tracer.equal_to(b"\x01\x02\x03\x04\x05"));

        let mut tracer = tracer.reset();
        let _ = MockFormatter{}.begin_object(&mut tracer);
        let _ = MockFormatter{}.begin_object_key(&mut tracer, true);
        let _ = MockFormatter{}.write_string_fragment(&mut tracer, "key");
        let _ = MockFormatter{}.end_object_key(&mut tracer);
        let _ = MockFormatter{}.begin_object_value(&mut tracer);
        let _ = MockFormatter{}.write_string_fragment(&mut tracer, "value");
        let _ = MockFormatter{}.end_object_value(&mut tracer);
        let _ = MockFormatter{}.end_object(&mut tracer);
        assert!(tracer.equal_to(b"{key=value}"));
    }
    
    #[test]
    fn escape(){
        let expect = b"\\\\";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::ReverseSolidus);
        assert!(tracer.equal_to(expect));

        let expect = b"\\,";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::Comma);
        assert!(tracer.equal_to(expect));

        let expect = b"\\=";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::Equals);
        assert!(tracer.equal_to(expect));

        let expect = b"\\[";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::OpeningBrackets);
        assert!(tracer.equal_to(expect));

        let expect = b"\\]";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::ClosingBrackets);
        assert!(tracer.equal_to(expect));

        let expect = b"\\{";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::OpeningCurlyBrackets);
        assert!(tracer.equal_to(expect));

        let expect = b"\\}";
        let mut tracer = Tracer::default();
        let _ = MockFormatter{}.write_char_escape(&mut tracer, CharEscape::ClosingCurlyBrackets);
        assert!(tracer.equal_to(expect));
    }
}

